package cn.iocoder.yudao;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.AccessSpecifier;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.JavadocComment;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.MemberValuePair;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr;

public class DDLGenerator {

	public static void main(String[] args) throws FileNotFoundException {

//		generateModuleSql("yudao-module-bpm");
//		generateModuleSql("yudao-module-crm");
//		generateModuleSql("yudao-module-erp");
//		generateModuleSql("yudao-module-mall");
//		generateModuleSql("yudao-module-mp");
//		generateModuleSql("yudao-module-pay");
//		generateModuleSql("yudao-module-report");
//		generateModuleSql("yudao-module-member");
		generateModuleSql("yudao-module-ai");

	}

	private static void generateModuleSql(String module) {
		try {
			Path projectRootPath = Paths.get(".").toAbsolutePath().getParent().getParent();
			Path modulePath = Paths.get(projectRootPath + File.separator + module);
			JavaParser javaParser = new JavaParser();
			File f = new File(projectRootPath + File.separator + "sql" + File.separator + "mysql" + File.separator
					+ module + ".sql");
			// 清空已有记录
			FileUtils.write(f, "", "UTF-8");
			// 列出所有java文件
			Files.walk(modulePath).forEach(filePath -> {
				if (Files.isRegularFile(filePath) && filePath.toString().endsWith(".java")) {
//				System.out.println(filePath);
					String fileName = filePath.getFileName().toString();
					String className = fileName.substring(0, fileName.length() - 5);
//				System.out.println("fileName:"+fileName+" className:"+className);
					try {
						ParseResult<CompilationUnit> parseResult = javaParser.parse(filePath);
						CompilationUnit cu = parseResult.getResult().get();
						ClassOrInterfaceDeclaration clsDec = cu.getClassByName(className).orElse(null);
						if (clsDec != null && isSubClassOfBaseDO(clsDec)) {
							//System.out.println(filePath);
							String sql = generateDDL(clsDec);
							//System.out.println(sql);
							FileUtils.write(f, sql, "UTF-8", true);
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 判断是否继承自 BaseDO
	 */
	private static boolean isSubClassOfBaseDO(ClassOrInterfaceDeclaration clsDec) {
		return clsDec.getExtendedTypes().stream().anyMatch(et -> et.getNameAsString().equals("BaseDO") || et.getNameAsString().equals("TenantBaseDO"));
	}

	private static String generateDDL(ClassOrInterfaceDeclaration classDecl) {
		StringBuilder sql = new StringBuilder();
		// 获取 @TableName 注解
		AnnotationExpr tableNameAnnotation = classDecl.getAnnotationByName("TableName").orElse(null);
		if (tableNameAnnotation == null) {
			System.err.println("@TableName 注解未找到");
		}
		String tableName = null;
		if (tableNameAnnotation instanceof SingleMemberAnnotationExpr) { // 处理单成员注解表达式
			// 获取 value 属性值
			SingleMemberAnnotationExpr valueExpr = tableNameAnnotation.asSingleMemberAnnotationExpr();
			tableName = valueExpr.getMemberValue().asStringLiteralExpr().getValue();

		} else if (tableNameAnnotation instanceof NormalAnnotationExpr) { // 处理普通注解表达式
			NormalAnnotationExpr normalAnnotationExpr = tableNameAnnotation.asNormalAnnotationExpr();
			for (MemberValuePair memberValuePair : normalAnnotationExpr.getPairs()) {
				String name = memberValuePair.getNameAsString();
				// 处理每个成员
				if ("value".equals(name)) {
					String value = memberValuePair.getValue().asStringLiteralExpr().getValue();
					tableName = value.toString();
				}
			}

		} else {
			throw new IllegalStateException(
					"Unsupported annotation expression type: " + tableNameAnnotation.getClass());
		}
		System.out.println("drop table "+tableName+";");
		sql.append("CREATE TABLE IF NOT EXISTS `").append(tableName).append("` (\n");
//		sql.append("CREATE TABLE ").append(tableName).append(" (\n");

		String pkField = null;
		// 遍历类中的字段声明
		for (FieldDeclaration fieldDecl : classDecl.getFields()) {
			// public类型的字段跳过
			if (AccessSpecifier.PUBLIC == fieldDecl.getAccessSpecifier()) {
				continue;
			}
			AnnotationExpr tableId = fieldDecl.getAnnotationByName("TableId").orElse(null);
			// 只有一个变量，取第一个
			VariableDeclarator variable = fieldDecl.getVariable(0);
			// 获取字段名称
			String fieldName = camelToUnderscore(variable.getNameAsString());

			// 获取字段类型
			String fieldType = variable.getType().toString();
			boolean isPrimaryKey = false;
			if (tableId != null) {
				isPrimaryKey = true;
				pkField = fieldName;
			}
			// 没有使用TableField注解
//        	fieldDecl.getAnnotationByName("TableField");

			// 映射 Java 类型到 SQL 类型
			String sqlType = mapJavaTypeToSqlType(fieldType);
			//System.out.println(fieldDecl.getJavadocComment());
			String comment = "";
			JavadocComment javadoc = fieldDecl.getJavadocComment().orElse(null);
			if (javadoc != null) {
				String[] comments = javadoc.getContent().replaceAll("\\*", "").split(System.lineSeparator());
				comment = Arrays.stream(comments).filter(str -> StringUtils.isNotBlank(str)).findFirst().orElse(null);
			}

			// 生成字段定义
			sql.append("  ").append(fieldName).append(" ").append(sqlType).append(" ");
			
			if("bit".equals(sqlType)) {
				sql.append("DEFAULT b'1' NOT NULL");
			}
			if (isPrimaryKey) {
				sql.append("NOT NULL AUTO_INCREMENT ");
			}
			if (StringUtils.isNotBlank(comment)) {
				sql.append(" COMMENT '").append(comment.trim()).append("' ");
			}
			sql.append(",\n");

		}
		sql.append("  tenant_id bigint not null default  0,\n");
		sql.append("  creator VARCHAR(64) COMMENT '创建者',\n");
		sql.append("  create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',\n");
		sql.append("  updater VARCHAR(64) COMMENT '更新者',\n");
		sql.append("  update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',\n");
		sql.append("  deleted bit DEFAULT b'0' NOT NULL COMMENT '是否删除'");

		// 添加主键约束
		if (StringUtils.isNotBlank(pkField)) {
			sql.append(",\n");
			sql.append("  PRIMARY KEY (`").append(pkField).append("`)\n");
		}else {
			sql.append("\n");
		}

		// 结束 SQL 语句
		sql.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n");

		return sql.toString();
	}

	private static String mapJavaTypeToSqlType(String fieldType) {
		if ("Long".equals(fieldType) || "long".equals(fieldType)) {
			return "BIGINT";
		} else if ("String".equals(fieldType)) {
			return "VARCHAR(255)";
		} else if ("LocalDateTime".equals(fieldType) || "Date".equals(fieldType)) {
			return "DATETIME";
		} else if ("Integer".equals(fieldType) || "int".equals(fieldType)) {
			return "INT";
		} else if ("Float".equals(fieldType) || "float".equals(fieldType)) {
			return "FLOAT";
		} else if ("Double".equals(fieldType) || "double".equals(fieldType)) {
			return "DOUBLE";
		} else if ("Boolean".equals(fieldType) || "boolean".equals(fieldType)) {
			return "bit";
		} else if ("Byte".equals(fieldType) || "byte".equals(fieldType)) {
			return "TINYINT";
		} else if ("Short".equals(fieldType) || "short".equals(fieldType)) {
			return "SMALLINT";
		} else if ("Character".equals(fieldType) || "char".equals(fieldType)) {
			return "CHAR(1)";
		} else {
			// throw new IllegalArgumentException("不支持的数据类型：" + fieldType);
			// 默认都使用varchar，JSON类型测试数据库版本太低不支持
			return "VARCHAR(255)";
		}
	}

	/**
	 * 将驼峰命名法转换为下划线命名法
	 *
	 * @param camelCaseStr 驼峰命名法的字符串
	 * @return 下划线命名法的字符串
	 */
	public static String camelToUnderscore(String camelCaseStr) {
		// 匹配大写字母前的字符
		Pattern pattern = Pattern.compile("(?<=[a-z])(?=[A-Z])");
		// 替换匹配到的大写字母前的字符加上下划线，并将其转换为小写
		return pattern.matcher(camelCaseStr).replaceAll("_").toLowerCase();
	}
}
